package com.example.spider.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.shoulder.batch.progress.BatchProgressCache;
import org.shoulder.batch.progress.BatchProgressRecord;
import org.shoulder.batch.progress.Progress;
import org.shoulder.batch.progress.ProgressStatusEnum;
import org.shoulder.core.util.JsonUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.text.DecimalFormat;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * http://localhost:8080/progress/query/{xx}
 * http://localhost:8080/progress/allTaskId
 * http://localhost:8080/progress/allProgress
 * <p>
 * http://localhost:8080/progress/query/validateAllProx y_USABLE_IP_PROXY_REPO
 * http://localhost:8080/progress/query/validateAllProxy_STABLE_IP_PROXY_REPO
 * http://localhost:8080/progress/query/validateAllProxy_SOURCE_IP_PROXY_REPO
 * <p>
 * http://localhost:8080/progressShow.html?progressId=validateAllProxy_STABLE_IP_PROXY_REPO
 * http://localhost:8080/index.html
 */
@RequestMapping("progress")
@Controller
public class ProgressController {

    public static String getProgressUrl(String taskId) {
        return "http://localhost:8080/progressShow.html?progressId=" + taskId;
    }

    @Autowired
    private BatchProgressCache progressCache;

    @Nullable
    private static String generateProgressJson(BatchProgressRecord record) {
        if (record == null) {
            return "无此任务";
        }

        Map<String, Object> map = new HashMap<>();
        String statusName = ProgressStatusEnum.values()[record.getStatus()].getName();
        String alreadyCostTime = formatDurationCN(record.calculateProcessedTime(), true, true);
        String estimatedRemaining = formatDurationCN(record.calculateTimeLeft(), true, true);
        String startTime = record.getStartTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        DecimalFormat df = new DecimalFormat("#.00");
        String costPercent = df.format(record.calculateProgress() * 100);

        map.put("id", record.getId());
        map.put("时间", startTime + " | 已运行 " + alreadyCostTime + " " + " | 剩余 " + (record.getTotal() - record.getProcessed()) + "，预估还需：" + estimatedRemaining);
        map.put("状态", statusName + " - " + record.getProcessed() + " / " + record.getTotal() + " (" + costPercent + "%)");
        return JsonUtils.toJson(map, ObjectMapper::writerWithDefaultPrettyPrinter);
    }

    private static Map<String, Object> toApiModel(BatchProgressRecord record) {
        Map<String, Object> map = new HashMap<>();
        if (record == null) {
            return map;
        }
        String statusName = ProgressStatusEnum.values()[record.getStatus()].getName();
        String alreadyCostTime = formatDurationCN(record.calculateProcessedTime(), true, true);
        String estimatedRemaining = formatDurationCN(record.calculateTimeLeft(), true, true);
        String startTime = Optional.ofNullable(record.getStartTime()).map(t -> t.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))).orElse("");
        DecimalFormat df = new DecimalFormat("#.00");
        String percent = df.format(record.calculateProgress() * 100);

        map.put("id", record.getId());
        map.put("status", statusName);
        map.put("alreadyCostTime", alreadyCostTime);
        map.put("estimatedRemaining", estimatedRemaining);
        map.put("startTime", startTime);
        map.put("percent", percent + "%");
        map.put("left", record.getTotal() - record.getProcessed());
        map.put("processed", record.getProcessed());
        map.put("total", record.getTotal());
        return map;
    }

    @ResponseBody
    @RequestMapping(value = "allTaskId", produces = "text/plain; version=0.0.4; charset=utf-8")
    public String allTaskId() {
        return String.join(",\n", progressCache.getAllTaskProgressId());
    }

    @ResponseBody
    @RequestMapping(value = "allProgress", produces = "text/plain; version=0.0.4; charset=utf-8")
    public String allProgress() {
        return progressCache.getAllTaskProgress().values().stream()
                .map(Progress::getBatchProgress)
                .map(ProgressController::generateProgressJson)
                .collect(Collectors.joining("\n\n\n"));
    }

    @ResponseBody
    @RequestMapping(value = "/query/{id}", produces = "text/plain; version=0.0.4; charset=utf-8")
    public String progresses(@PathVariable("id") String id) {
        BatchProgressRecord record = progressCache.getProgress(id).getBatchProgress();
        return generateProgressJson(record) ;
    }

    @ResponseBody
    @RequestMapping(value = "/api/queryAll")
    public List<Map<String, Object>> queryAllProgressApi() {
        return progressCache.getAllTaskProgress().values().stream()
                .map(Progress::getBatchProgress)
                .map(ProgressController::toApiModel)
                .collect(Collectors.toList());
    }

    @ResponseBody
    @RequestMapping(value = "/api/query/{id}")
    public Map<String, Object> queryProgressByIdApi(@PathVariable("id") String id) {
        BatchProgressRecord record = progressCache.getProgress(id).getBatchProgress();
        return toApiModel(record);
    }

    private static String formatDurationCN(long durationMillis, boolean suppressLeadingZeroElements, boolean suppressTrailingZeroElements) {
        String duration = DurationFormatUtils.formatDuration(durationMillis, "d' 天 'H' 小时 'm' 分钟 's' 秒'");
        String tmp;
        if (suppressLeadingZeroElements) {
            duration = " " + duration;
            tmp = StringUtils.replaceOnce(duration, " 0 天", "");
            if (tmp.length() != duration.length()) {
                duration = tmp;
                tmp = StringUtils.replaceOnce(tmp, " 0 小时", "");
                if (tmp.length() != duration.length()) {
                    tmp = StringUtils.replaceOnce(tmp, " 0 分钟", "");
                    duration = tmp;
                }
            }

            if (!duration.isEmpty()) {
                duration = duration.substring(1);
            }
        }

        if (suppressTrailingZeroElements) {
            tmp = StringUtils.replaceOnce(duration, " 0 秒", "");
            if (tmp.length() != duration.length()) {
                duration = tmp;
                tmp = StringUtils.replaceOnce(tmp, " 0 分钟", "");
                if (tmp.length() != duration.length()) {
                    duration = tmp;
                    tmp = StringUtils.replaceOnce(tmp, " 0 小时", "");
                    if (tmp.length() != duration.length()) {
                        duration = StringUtils.replaceOnce(tmp, " 0 天", "");
                    }
                }
            }
        }
        return duration;
    }
}
